﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public class MeRetain
{
		
		public object Data;
		private int mRetainCount;

		public int RetainCount {
				get {
						return mRetainCount;
				}
		}

		public void Retain ()
		{
				++mRetainCount;
		}

		public void Release ()
		{
				--mRetainCount;
		}

		public void Dealloc ()
		{
				mRetainCount = - 128;
		}
}

public delegate void CallBack ();

public delegate void CallBackData<T> (T data);

public delegate void CallBackDatas<S, T> (S s, T data);

public delegate void CallBackDatask<S, K, T> (S s, K k, T data);

public delegate T InstanceData<T> ();

public delegate I InstanceData<I, T> (T data);

public delegate void InstanceCallBack<I, T> (T data, CallBackData<I> callBack);

public delegate IEnumerator InstanceCallBackAsyn<I, T> (T data, CallBackData<I> callBack);

public class MeCache
{

		static MeCache Instance;
		Dictionary<string, MeRetain> mCacheRetains = new Dictionary<string, MeRetain> ();
		Dictionary<string, List<object>> mCacheCallBackDatas = new Dictionary<string, List<object>> ();

		public static MeCache Shared ()
		{
				if (Instance == null) {
						Instance = new MeCache ();
				}

				return Instance;
		}

		public MeRetain RetainData<T> (string dataKey, CallBackData<T> callBackData, InstanceCallBack<T, string> instanceCallBack)
		{
				return RetainData<T> (dataKey, callBackData, callBackData, instanceCallBack);
		}

		public MeRetain RetainData<T> (string dataKey, CallBackData<T> callBackData, CallBackData<T> callBackDataAsyn, InstanceCallBack<T, string> instanceCallBack)
		{
				MeRetain meRetain;
				mCacheRetains.TryGetValue (dataKey, out meRetain);
				if (meRetain != null) {
						if (meRetain.RetainCount >= 0) {
								meRetain.Retain ();
								if (meRetain.Data == null) {
										AddCallBackData (dataKey, callBackData);
					
								} else {
										callBackDataAsyn ((T)meRetain.Data);
								}

								return meRetain;

						} else {
								mCacheRetains.Remove (dataKey);
						}
				}

				meRetain = new MeRetain ();
				meRetain.Retain ();
				mCacheRetains.Add (dataKey, meRetain);
				
				
				AddCallBackData (dataKey, callBackData);
				instanceCallBack (dataKey, (data) => {
						if (data == null) {
								meRetain.Dealloc ();

						} else {
								meRetain.Data = data;
						}

						DoCallBackData<T> (dataKey, data);});

				return meRetain;
		}

		private void AddCallBackData (string dataKey, object callBackData)
		{
				lock (mCacheCallBackDatas) {
						List<object> callBackDatas;
						mCacheCallBackDatas.TryGetValue (dataKey, out callBackDatas);
						if (callBackDatas == null) {
								callBackDatas = new List<object> ();
								mCacheCallBackDatas.Add (dataKey, callBackDatas);
						}
				
						callBackDatas.Add (callBackData);
				}
		}

		private void DoCallBackData<T> (string dataKey, T data)
		{
				lock (mCacheCallBackDatas) {
						List<object> callBackDatas;
						mCacheCallBackDatas.TryGetValue (dataKey, out callBackDatas);
						if (callBackDatas != null) {
								foreach (object callBackData in callBackDatas) {
										(callBackData as CallBackData<T>) (data);
								}

								mCacheCallBackDatas.Remove (dataKey);
						}
				}
		}
	
		public void AutoRelease ()
		{
				List<string> releaseKeys = new List<string> ();
				foreach (KeyValuePair<string, MeRetain> keyValuePair in mCacheRetains) {
						if (keyValuePair.Value.RetainCount <= 0) {
								releaseKeys.Add (keyValuePair.Key);
						}
				}

				foreach (string releaseKey in releaseKeys) {
						mCacheRetains.Remove (releaseKey);
				}
		}

		public void Clear ()
		{
				mCacheRetains.Clear ();
		}
}
